﻿
Imports std

Public Module rcclient

    Private talkpool As talkpoolDefinition = Nothing

    Public remotehost As String = Nothing
    Public remoteport As Int32 = npos

    Public Delegate Sub callbackDelegate(Of rtnType As fromBytes)(ByVal rtn As rtnType)
    Public Delegate Sub callbackDelegate2(Of rtnType As fromBytes)(ByVal rtn() As rtnType)

    Private Class invokeInfo(Of rtnType As fromBytes)
        Public funcname As String
        Public args() As toBytes
        Public rtns() As rtnType
        Public host As String
        Public port As Int32
        Public callback As callbackDelegate(Of rtnType)
        Public callback2 As callbackDelegate2(Of rtnType)

        Public Sub New(ByVal funcname As String, ByVal host As String, ByVal port As Int32, _
                       ByVal callback As callbackDelegate(Of rtnType), _
                       ByVal callback2 As callbackDelegate2(Of rtnType), _
                       ByVal ParamArray args() As toBytes)
            copy(Me.funcname, funcname)
            copy(Me.host, host)
            Me.port = port
            Me.callback = callback
            Me.callback2 = callback2
            If Not args Is Nothing Then
                ReDim Me.args(args.Length() - 1)
                Dim i As Int64
                For i = 0 To args.Length() - 1
                    copy(Me.args(i), args(i))
                Next
            Else
                Me.args = Nothing
            End If
        End Sub

        Public Function rtn() As rtnType
            If Not rtns Is Nothing AndAlso rtns.Length > 0 Then
                Return rtns(0)
            Else
                Return Nothing
            End If
        End Function
    End Class

    Private Sub initialTalk(ByVal talk As tcpTalk, ByVal host As String, ByVal port As Int32)
        assert(Not talk Is Nothing, "talk is a nothing tcpTalk instance.")
        talk.host = host
        talk.port = port
        talk.sendData.clear()
    End Sub

    Private Sub invoke(Of rtnType As fromBytes)(ByVal info As invokeInfo(Of rtnType))
        Dim index As Int64 = npos
        Dim talk As tcpTalk = Nothing
        If talkpool.getfree(index, talk) Then
            initialTalk(talk, info.host, info.port)
            If protocol.set(talk.sendData, info.funcname, info.args) Then
                If talk.talk() Then
                    ReDim info.rtns(talk.receiveData().size() - 1)
                    Dim i As Int64
                    For i = 0 To talk.receiveData().size() - 1
                        Dim bt As bytes = Nothing
                        If Not talk.receiveData().take(i, bt) Then
                            Exit For
                        Else
                            info.rtns(i) = alloc(Of rtnType)()
                            info.rtns(i).frombytes(bt)
                        End If
                    Next
                    If i = talk.receiveData().size() Then
                        If Not info.callback Is Nothing Then
                            info.callback(info.rtn())
                        ElseIf Not info.callback2 Is Nothing Then
                            info.callback2(info.rtns)
                        End If
                    Else
                        raiseError("do not get valid return value from server, ignore request." _
                                   , errorHandle.errorType.warning)
                    End If
                Else
                    raiseError("cannot talk with server, host " + info.host + ", port " + Convert.ToString(info.port) _
                               + ", ignore request.", errorHandle.errorType.warning)
                End If
            Else
                raiseError("cannot set talk.sendData with funcname " + info.funcname + " and argument, ignore request." _
                           , errorHandle.errorType.warning)
            End If

            talkpool.release(index)
        Else
            raiseError("cannot get free tcpTalk instance from talk pool, ignore request." _
                       , errorHandle.errorType.warning)
        End If
    End Sub

    Public Function invoke(Of rtnType As fromBytes)(ByVal funcname As String, _
                           ByVal host As String, ByVal port As Int32, _
                           ByVal callback As callbackDelegate(Of rtnType), _
                           ByVal ParamArray args() As toBytes) As rtnType
        Dim info As invokeInfo(Of rtnType) = Nothing
        info = New invokeInfo(Of rtnType)(funcname, host, port, callback, Nothing, args)
        If callback Is Nothing Then
            invoke(info)
            Return info.rtn()
        Else
            startThread(AddressOf invoke, info)
            Return Nothing
        End If
    End Function

    Public Function invoke2(Of rtnType As fromBytes)(ByVal funcname As String, _
                            ByVal host As String, ByVal port As Int32, _
                            ByVal callback2 As callbackDelegate2(Of rtnType), _
                            ByVal ParamArray args() As toBytes) As rtnType()
        Dim info As invokeInfo(Of rtnType) = Nothing
        info = New invokeInfo(Of rtnType)(funcname, host, port, Nothing, callback2, args)
        If callback2 Is Nothing Then
            invoke(info)
            Return info.rtns
        Else
            startThread(AddressOf invoke, info)
            Return Nothing
        End If
    End Function

    Public Function invoke(Of rtnType As fromBytes)(ByVal funcname As String, _
                           ByVal callback As callbackDelegate(Of rtnType), _
                           ByVal ParamArray args() As toBytes) As rtnType
        Return invoke(Of rtnType)(funcname, remotehost, remoteport, callback, args)
    End Function

    Public Function invoke2(Of rtnType As fromBytes)(ByVal funcname As String, _
                            ByVal callback2 As callbackDelegate2(Of rtnType), _
                            ByVal ParamArray args() As toBytes) As rtnType()
        Return invoke2(Of rtnType)(funcname, remotehost, remoteport, callback2, args)
    End Function

    Public Function invoke(Of rtnType As fromBytes)(ByVal funcname As String, _
                           ByVal host As String, ByVal port As Int32, _
                           ByVal ParamArray args() As toBytes) As rtnType
        Return invoke(Of rtnType)(funcname, host, port, Nothing, args)
    End Function

    Public Function invoke2(Of rtnType As fromBytes)(ByVal funcname As String, _
                            ByVal host As String, ByVal port As Int32, _
                            ByVal ParamArray args() As toBytes) As rtnType()
        Return invoke2(Of rtnType)(funcname, host, port, Nothing, args)
    End Function

    Public Function invoke(Of rtnType As fromBytes)(ByVal funcname As String, _
                           ByVal ParamArray args() As toBytes) As rtnType
        Return invoke(Of rtnType)(funcname, remotehost, remoteport, args)
    End Function

    Public Function invoke2(Of rtnType As fromBytes)(ByVal funcname As String, _
                            ByVal ParamArray args() As toBytes) As rtnType()
        Return invoke2(Of rtnType)(funcname, remotehost, remoteport, args)
    End Function

    Public Sub initial(ByVal host As String, ByVal port As Int32)
        copy(remotehost, host)
        copy(remoteport, port)
    End Sub

    Sub New()
        talkpool = New talkpoolDefinition()
    End Sub

    Public Sub close()
        talkpool.close()
    End Sub

    Public Sub unittest()
        initial(network.localhost, share.unittest.port)
        Console.WriteLine(longTime())
        Dim i As Int64
        For i = -1000 To 999
            Console.WriteLine(invoke(Of bytesInt32)(share.unittest.unittestInvokeName, _
                                                    New bytesString(Convert.ToString(i))))
        Next
        Console.WriteLine(longTime())
    End Sub

    Public Sub performanceTest()
        Dim testsize As Int64 = 1024
        If isreleasebuild() Then
            testsize *= 20
        End If

        initial(network.localhost, share.unittest.port)
        raiseError("******* automatical remotecall perfTest *******", errorHandle.errorType.application)

        Dim i As Int64
        For i = 0 To testsize - 1
            If rnd(0, 2, True) = 0 Then
                raiseError("remotecall with argument as a guid string.")
                raiseError("remotecall returns " _
                           + Convert.ToString(invoke(Of bytesInt32)(share.unittest.unittestInvokeName, _
                                                                    New bytesString(createGUID()))))
            Else
                raiseError("remotecall with argument " + Convert.ToString(i))
                raiseError("remotecall returns " _
                           + Convert.ToString(invoke(Of bytesInt32)(share.unittest.unittestInvokeName, _
                                                                    New bytesString(Convert.ToString(i)))))
            End If
        Next

        close()
        raiseError("********* finish automatical perfTest *********", errorHandle.errorType.application)
    End Sub
End Module
